import { Project, Node } from "ts-morph";
import fs from "fs";
import path from "path";
import JSON5 from "json5"; // 引入 json5 库
import { fileURLToPath } from "url"; // 引入 fileURLToPath
import { generateFileName } from "./tools/JsonName.js";

// 获取当前文件的路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// 创建一个新的 ts-morph 项目
const project = new Project();

// 目标文件夹路径
// const targetDir = "E:\\OpenHarmony\\Compare\\arkts_gt_libs"; // groudtruth的50个包
const targetDir = "E:\\OpenHarmony\\Compare\\test"; // pako
// const targetDir = "E:\\OpenHarmony\\Compare\\test"; // 80个规范化移植

// 定义需要忽略的文件夹名称
const IGNORED_SUBDIRECTORIES = [

    //"dist",
    "example",
    "examples",
    "test",
    "__tests__",
    "docs",
    "documentation",
    "build", //有些库只有这个里面有js文件
    "perf",
    "scripts",
    "benchmark",
    "__benchmark__",
    "coverage",
    "config",
    "mocks",
    "fixtures",
];

// 定义存储函数的 JSON 结构
const functionsData = {
    totalFunctions: 0,
    functions: [],
};

// 支持的输入文件类型
const SUPPORTED_EXTENSIONS = [".js",".cjs", ".mjs",".ts", ".ets"];

//保证函数体的内容符合标准的大括号封装格式
function ensureBraces(str) {
    // 定义正则表达式
    const regex = /^\{[\s\S]*}$/;
    // 测试字符串是否已符合要求
    if (!regex.test(str)) {
        // 如果不符合，就在字符串两边加上大括号
        str = '{' + str + '}';
    }
    return str;
}
/**
 * 递归遍历文件夹中的所有文件
 * @param {string} dirPath - 文件夹路径
 * @param {string} libraryName - 库名称
 * @param {string} libraryVersion - 库版本
 */
function traverseDirectory(dirPath, libraryName, libraryVersion) {
    const items = fs.readdirSync(dirPath);

    for (const item of items) {
        const itemPath = path.join(dirPath, item);
        const stat = fs.statSync(itemPath);

        if (stat.isDirectory()) {
            // 如果是文件夹，递归遍历
            // 如果是文件夹，检查是否在忽略列表中
            if (IGNORED_SUBDIRECTORIES.includes(item)) {
                console.log(`忽略子文件夹: ${itemPath}`);
                continue;
            }
            traverseDirectory(itemPath, libraryName, libraryVersion);
        } else if (stat.isFile() && SUPPORTED_EXTENSIONS.includes(path.extname(item).toLowerCase())) {
            const fileName = itemPath.split(path.sep).at(-1);
            // 检查是否是 .xx.js 文件
            const isDotXxJs = /\.(?!.*\.).+\.js$/.test(fileName);
            if (isDotXxJs) {
                continue;  // 如果文件名符合 .xx.js 模式，跳过处理
            }
            // 检查是否是需要特殊忽略的文件
            const specialIgnoredPatterns = [
                /\.d\.ts$/,
                /\.test\.ts$/,
                /\.min\.js$/,
                /\.prod\.js$/,
                /\.light\.js$/,
                /\.full\.js$/,
                /\.eslintrc\.js$/,
                /\.config\.js$/,
                /\.config\.ts$/,
                /\.conf\.js$/
            ];
            if (specialIgnoredPatterns.some(pattern => pattern.test(fileName))) {
                continue;  // 如果文件名匹配任何特殊的忽略模式，跳过处理
            }
            const ignoreFileName = ["Gruntfile.js"]
            if (ignoreFileName.includes(fileName)){
                continue;
            }
            processFile(itemPath, libraryName, libraryVersion);
        }
    }
}

/**
 * 处理单个文件，提取函数信息
 * @param {string} filePath - 文件路径
 * @param {string} libraryName - 库名称
 * @param {string} libraryVersion - 库版本
 */
function processFile(filePath, libraryName, libraryVersion) {
    // 获取文件的相对路径
    const relativePath = path.relative(targetDir, filePath);

    let sourceFilePath = filePath;

    // 如果是 .ets 文件，复制到一个临时 .ts 文件
    if (filePath.endsWith(".ets")) {
        const tempFilePath = filePath.replace(/\.ets$/, `.temp_${Date.now()}.ts`); // 使用时间戳确保唯一性

        // 检查源文件是否存在
        if (!fs.existsSync(filePath)) {
            console.error(`文件不存在: ${filePath}`);
            return;
        }

        try {
            fs.copyFileSync(filePath, tempFilePath); // 复制文件
            sourceFilePath = tempFilePath;
        } catch (error) {
            console.error(`复制文件失败: ${filePath} -> ${tempFilePath}`, error);
            return;
        }
    }

    // 添加源文件到项目中
    const sourceFile = project.addSourceFileAtPath(sourceFilePath);

    // 遍历 AST 提取所有函数
    sourceFile.forEachDescendant(node => {
        if (Node.isFunctionDeclaration(node)) {
            //检查当前的FunctionDeclaration节点下面有没有FunctionDeclaration节点，如果有就是函数嵌套，就跳过外层的函数只保留内层的函数定义
            //先看这个函数是不是嵌套定义的，如果是就不进行提取了
            const LineNumber  =  node.getEndLineNumber()- node.getStartLineNumber()
            let isNestedFunction = false
            node.forEachDescendant(
                (child,traversal)=>{
                    if(Node.isFunctionDeclaration(child)){
                        console.log("nested funcions")
                        isNestedFunction = true
                        traversal.stop()
                    }
                }
            )
            if(LineNumber > 1000 || isNestedFunction){
                return;
            }


            // 处理命名函数
            const functionName = node.getName() || "anonymous";
            const functionBody = node.getBody()?.getText() || "{}";

            addFunctionToData(functionName, functionBody, "function", libraryName, libraryVersion, relativePath);
        } else if (Node.isArrowFunction(node)) {
            // 处理箭头函数
            const LineNumber  =  node.getEndLineNumber()- node.getStartLineNumber()
            if(LineNumber > 1000 ){
                return;
            }
            const functionName = "anonymous";
            const functionBody = node.getBody().getText()|| "{}";

            addFunctionToData(functionName, functionBody, "arrow function", libraryName, libraryVersion, relativePath);
        } else if (Node.isMethodDeclaration(node)) {
            // 处理对象方法和类方法
            const functionName = node.getName() || "anonymous";
            const functionBody = node.getBody()?.getText() || "{}";

            addFunctionToData(functionName, functionBody, "method", libraryName, libraryVersion, relativePath);
        }
        //ArkTS不支持函数表达式，使用箭头函数。但是有的arkts没有做适配，会有一些是函数嵌套定义。

            else if (Node.isFunctionExpression(node)) {
            // 处理匿名函数
            if (Node.isFunctionExpression(node)) {
                // 这里是函数表达式，现在获取它的父节点
                const parentNode = node.getParent();
                //如果父节点的类型是ParenthesizedExpression，那就再获取父节点的父节点，判断类型是不是CallExpression
                if (Node.isParenthesizedExpression(parentNode)){
                    const rootNode = parentNode.getParent();
                    if (Node.isCallExpression(rootNode)){
                        return;
                    }
                }
            }
            const LineNumber  =  node.getEndLineNumber()- node.getStartLineNumber()
            if(LineNumber > 1000 ){
                return;
            }
            const functionName = "anonymous";
            const functionBody = node.getBody()?.getText() || "{}";

            addFunctionToData(functionName, functionBody, "function expression", libraryName, libraryVersion, relativePath);
        }
    });

    // 如果是 .ets 文件，删除临时文件
    if (filePath.endsWith(".ets")) {
        try {
            fs.unlinkSync(sourceFilePath); // 删除临时文件
        } catch (error) {
            console.error(`删除临时文件失败: ${sourceFilePath}`, error);
        }
    }
}

/**
 * 将函数信息添加到 functionsData 中
 * @param {string} name - 函数名称
 * @param {string} body - 函数体
 * @param {string} type - 函数类型
 * @param {string} library - 库名称
 * @param {string} version - 库版本
 * @param {string} relativePath - 相对路径
 */
function addFunctionToData(name, body, type, library, version, relativePath) {
    //如果函数的body为空，那么就不加入到functionsData中

    body = ensureBraces(body)
    if (body === "{}") {
        return;
    }
    functionsData.functions.push({
        type: type,
        name: name,
        body: body,
        library: library,
        version: version,
        relativePath: relativePath.replace(/\\/g, "/"), // 统一使用正斜杠
    });
}

/**
 * 处理单个库
 * @param {string} libraryName - 库名称
 * @param {string} libraryPath - 库路径
 */
function processLibrary(libraryName, libraryPath) {
    // 检查是否存在 oh-package.json5 文件
    let libraryVersion = "unknown"; // 默认版本为 unknown
    const ohPackageJson5Path = path.join(libraryPath, "oh-package.json5");

    if (fs.existsSync(ohPackageJson5Path)) {
        try {
            // 读取 oh-package.json5 文件内容
            const ohPackageJsonContent = fs.readFileSync(ohPackageJson5Path, "utf-8");

            // 使用 json5 解析文件内容
            const ohPackageJson = JSON5.parse(ohPackageJsonContent);
            libraryVersion = ohPackageJson.version || "unknown"; // 提取 version 字段
        } catch (error) {
            console.error(`解析 oh-package.json5 文件失败: ${ohPackageJson5Path}`, error);
        }
    }

    console.log(`处理三方库: ${libraryName} (版本: ${libraryVersion})`);

    // 递归遍历三方库文件夹中的所有文件
    //如果文件夹的名字为Component，就直接跳过对该文件夹的遍历

    traverseDirectory(libraryPath, libraryName, libraryVersion);
}

// 主函数
function main() {
    // 读取目标文件夹中的所有子文件夹
    const libraries = fs.readdirSync(targetDir, { withFileTypes: true })
        .filter(dirent => dirent.isDirectory() && !dirent.name.startsWith(".")) // 忽略隐藏文件夹
        .map(dirent => dirent.name);

    // 遍历每个三方库
    libraries.forEach(libraryName => {
        const libraryPath = path.join(targetDir, libraryName);
        if (libraryName.startsWith('@')) {
            // 读取 @ 开头的子文件夹中的所有非隐藏子文件夹
            const subLibraries = fs.readdirSync(libraryPath, { withFileTypes: true })
                .filter(dirent => dirent.isDirectory() && !dirent.name.startsWith("."))
                .map(dirent => dirent.name);
            subLibraries.forEach(subLibraryName => {
                const subLibraryPath = path.join(libraryPath, subLibraryName);
                processLibrary(subLibraryName, subLibraryPath);
            });
        } else {
            processLibrary(libraryName, libraryPath);
        }
    });

    // 更新函数总数
    functionsData.totalFunctions = functionsData.functions.length;

    // 将函数数据写入 JSON 文件
    const filename = generateFileName("pakoArkts");
    const outputFilePath = path.join(__dirname, filename);
    fs.writeFileSync(outputFilePath, JSON.stringify(functionsData, null, 2));
    console.log(`Functions extracted and saved to ${outputFilePath}`);
}

// 运行主函数
main();
